 /*******************************************************************************
  * Copyright (c) 2003, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/

 package org.eclipse.osgi.framework.internal.core;

 import java.io.File ;
 import java.io.InputStream ;
 import java.security.*;
 import java.util.*;
 import org.eclipse.osgi.event.BatchBundleListener;
 import org.eclipse.osgi.framework.debug.Debug;
 import org.eclipse.osgi.framework.eventmgr.EventDispatcher;
 import org.eclipse.osgi.framework.eventmgr.EventListeners;
 import org.eclipse.osgi.internal.profile.Profile;
 import org.eclipse.osgi.util.NLS;
 import org.osgi.framework.*;

 /**
  * Bundle's execution context.
  *
  * This object is given out to bundles and wraps the internal
  * BundleContext object. It is destroyed when a bundle is stopped.
  */

 public class BundleContextImpl implements BundleContext, EventDispatcher {
     public static final String PROP_SCOPE_SERVICE_EVENTS = "osgi.scopeServiceEvents"; //$NON-NLS-1$
 public static final boolean scopeEvents = Boolean.valueOf(FrameworkProperties.getProperty(PROP_SCOPE_SERVICE_EVENTS, "true")).booleanValue(); //$NON-NLS-1$
 /** true if the bundle context is still valid */
     private boolean valid;

     /** Bundle object this context is associated with. */
     // This slot is accessed directly by the Framework instead of using
 // the getBundle() method because the Framework needs access to the bundle
 // even when the context is invalid while the close method is being called.
 protected BundleHost bundle;

     /** Internal framework object. */
     protected Framework framework;

     /** Services that bundle has used. Key is ServiceReference,
      Value is ServiceUse */
     protected Hashtable servicesInUse;

     /** Listener list for bundle's BundleListeners */
     protected EventListeners bundleEvent;

     /** Listener list for bundle's SynchronousBundleListeners */
     protected EventListeners bundleEventSync;

     /** Listener list for bundle's ServiceListeners */
     protected EventListeners serviceEvent;

     /** Listener list for bundle's FrameworkListeners */
     protected EventListeners frameworkEvent;

     /** The current instantiation of the activator. */
     protected BundleActivator activator;

     /** private object for locking */
     protected Object contextLock = new Object ();

     /**
      * Construct a BundleContext which wrappers the framework for a
      * bundle
      *
      * @param bundle The bundle we are wrapping.
      */
     protected BundleContextImpl(BundleHost bundle) {
         this.bundle = bundle;
         valid = true;
         framework = bundle.framework;
         bundleEvent = null;
         bundleEventSync = null;
         serviceEvent = null;
         frameworkEvent = null;
         servicesInUse = null;
         activator = null;
     }

     /**
      * Destroy the wrapper. This is called when the bundle is stopped.
      *
      */
     protected void close() {
         valid = false; /* invalidate context */

         synchronized (framework.serviceEvent) {
             if (serviceEvent != null) {
                 framework.serviceEvent.removeListener(this);
                 serviceEvent = null;
             }
         }
         synchronized (framework.frameworkEvent) {
             if (frameworkEvent != null) {
                 framework.frameworkEvent.removeListener(this);
                 frameworkEvent = null;
             }
         }
         synchronized (framework.bundleEvent) {
             if (bundleEvent != null) {
                 framework.bundleEvent.removeListener(this);
                 bundleEvent = null;
             }
         }
         synchronized (framework.bundleEventSync) {
             if (bundleEventSync != null) {
                 framework.bundleEventSync.removeListener(this);
                 bundleEventSync = null;
             }
         }

         /* service's registered by the bundle, if any, are unregistered. */
         ServiceReference[] publishedReferences = null;
         synchronized (framework.serviceRegistry) {
             publishedReferences = framework.serviceRegistry.lookupServiceReferences(this);
         }

         if (publishedReferences != null) {
             for (int i = 0; i < publishedReferences.length; i++) {
                 try {
                     ((ServiceReferenceImpl) publishedReferences[i]).registration.unregister();
                 } catch (IllegalStateException e) {
                     /* already unregistered */
                 }
             }
         }

         /* service's used by the bundle, if any, are released. */
         if (servicesInUse != null) {
             int usedSize;
             ServiceReference[] usedRefs = null;

             synchronized (servicesInUse) {
                 usedSize = servicesInUse.size();

                 if (usedSize > 0) {
                     if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
                         Debug.println("Releasing services"); //$NON-NLS-1$
 }

                     usedRefs = new ServiceReference[usedSize];

                     Enumeration refsEnum = servicesInUse.keys();
                     for (int i = 0; i < usedSize; i++) {
                         usedRefs[i] = (ServiceReference) refsEnum.nextElement();
                     }
                 }
             }

             for (int i = 0; i < usedSize; i++) {
                 ((ServiceReferenceImpl) usedRefs[i]).registration.releaseService(this);
             }

             servicesInUse = null;
         }

         bundle = null;
     }

     /**
      * Retrieve the value of the named environment property.
      *
      * @param key The name of the requested property.
      * @return The value of the requested property, or <code>null</code> if
      * the property is undefined.
      */
     public String getProperty(String key) {
         SecurityManager sm = System.getSecurityManager();

         if (sm != null) {
             sm.checkPropertyAccess(key);
         }

         return (framework.getProperty(key));
     }

     /**
      * Retrieve the Bundle object for the context bundle.
      *
      * @return The context bundle's Bundle object.
      */
     public org.osgi.framework.Bundle getBundle() {
         checkValid();

         return (bundle);
     }

     /**
      * Install a bundle from a location.
      *
      * The bundle is obtained from the location
      * parameter as interpreted by the framework
      * in an implementation dependent way. Typically, location
      * will most likely be a URL.
      *
      * @param location The location identifier of the bundle to install.
      * @return The Bundle object of the installed bundle.
      */
     public org.osgi.framework.Bundle installBundle(String location) throws BundleException {
         checkValid();
         //note AdminPermission is checked after bundle is loaded
 return framework.installBundle(location);
     }

     /**
      * Install a bundle from an InputStream.
      *
      * <p>This method performs all the steps listed in
      * {@link #installBundle(java.lang.String)}, except the
      * bundle's content will be read from the InputStream.
      * The location identifier specified will be used
      * as the identity of the bundle.
      *
      * @param location The location identifier of the bundle to install.
      * @param in The InputStream from which the bundle will be read.
      * @return The Bundle of the installed bundle.
      */
     public org.osgi.framework.Bundle installBundle(String location, InputStream in) throws BundleException {
         checkValid();
         //note AdminPermission is checked after bundle is loaded
 return framework.installBundle(location, in);
     }

     /**
      * Retrieve the bundle that has the given unique identifier.
      *
      * @param id The identifier of the bundle to retrieve.
      * @return A Bundle object, or <code>null</code>
      * if the identifier doesn't match any installed bundle.
      */
     public org.osgi.framework.Bundle getBundle(long id) {
         return (framework.getBundle(id));
     }

     /**
      * Retrieve the bundle that has the given location.
      *
      * @param location The location string of the bundle to retrieve.
      * @return A Bundle object, or <code>null</code>
      * if the location doesn't match any installed bundle.
      */
     public AbstractBundle getBundleByLocation(String location) {
         return (framework.getBundleByLocation(location));
     }

     /**
      * Retrieve a list of all installed bundles.
      * The list is valid at the time
      * of the call to getBundles, but the framework is a very dynamic
      * environment and bundles can be installed or uninstalled at anytime.
      *
      * @return An array of {@link AbstractBundle} objects, one
      * object per installed bundle.
      */
     public org.osgi.framework.Bundle[] getBundles() {
         return framework.getAllBundles();
     }

     /**
      * Add a service listener with a filter.
      * {@link ServiceListener}s are notified when a service has a lifecycle
      * state change.
      * See {@link #getServiceReferences(String, String) getServiceReferences}
      * for a description of the filter syntax.
      * The listener is added to the context bundle's list of listeners.
      * See {@link #getBundle() getBundle()}
      * for a definition of context bundle.
      *
      * <p>The listener is called if the filter criteria is met.
      * To filter based upon the class of the service, the filter
      * should reference the "objectClass" property.
      * If the filter paramater is <code>null</code>, all services
      * are considered to match the filter.
      * <p>If the Java runtime environment supports permissions, then additional
      * filtering is done.
      * {@link AbstractBundle#hasPermission(Object) Bundle.hasPermission} is called for the
      * bundle which defines the listener to validate that the listener has the
      * {@link ServicePermission} permission to <code>"get"</code> the service
      * using at least one of the named classes the service was registered under.
      *
      * @param listener The service listener to add.
      * @param filter The filter criteria.
      * @exception InvalidSyntaxException If the filter parameter contains
      * an invalid filter string which cannot be parsed.
      * @see ServiceEvent
      * @see ServiceListener
      * @exception java.lang.IllegalStateException
      * If the bundle context has stopped.
      */
     public void addServiceListener(ServiceListener listener, String filter) throws InvalidSyntaxException {
         checkValid();

         if (Debug.DEBUG && Debug.DEBUG_EVENTS) {
             String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(listener)); //$NON-NLS-1$
 Debug.println("addServiceListener[" + bundle + "](" + listenerName + ", \"" + filter + "\")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
 }

         ServiceListener filteredListener = new FilteredServiceListener(filter, listener, this);

         synchronized (framework.serviceEvent) {
             if (serviceEvent == null) {
                 serviceEvent = new EventListeners();
                 framework.serviceEvent.addListener(this, this);
             }

             serviceEvent.addListener(listener, filteredListener);
         }
     }

     /**
      * Add a service listener.
      *
      * <p>This method is the same as calling
      * {@link #addServiceListener(ServiceListener, String)}
      * with filter set to <code>null</code>.
      *
      * @see #addServiceListener(ServiceListener, String)
      */
     public void addServiceListener(ServiceListener listener) {
         try {
             addServiceListener(listener, null);
         } catch (InvalidSyntaxException e) {
             if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
                 Debug.println("InvalidSyntaxException w/ null filter" + e.getMessage()); //$NON-NLS-1$
 Debug.printStackTrace(e);
             }
         }
     }

     /**
      * Remove a service listener.
      * The listener is removed from the context bundle's list of listeners.
      * See {@link #getBundle() getBundle()}
      * for a definition of context bundle.
      *
      * <p>If this method is called with a listener which is not registered,
      * then this method does nothing.
      *
      * @param listener The service listener to remove.
      * @exception java.lang.IllegalStateException
      * If the bundle context has stopped.
      */
     public void removeServiceListener(ServiceListener listener) {
         checkValid();

         if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
             String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(listener)); //$NON-NLS-1$
 Debug.println("removeServiceListener[" + bundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }

         synchronized (framework.serviceEvent) {
             if (serviceEvent != null) {
                 serviceEvent.removeListener(listener);
             }
         }
     }

     /**
      * Add a bundle listener.
      * {@link BundleListener}s are notified when a bundle has a lifecycle
      * state change.
      * The listener is added to the context bundle's list of listeners.
      * See {@link #getBundle() getBundle()}
      * for a definition of context bundle.
      *
      * @param listener The bundle listener to add.
      * @exception java.lang.IllegalStateException
      * If the bundle context has stopped.
      * @see BundleEvent
      * @see BundleListener
      */
     public void addBundleListener(BundleListener listener) {
         checkValid();

         if (Debug.DEBUG && Debug.DEBUG_EVENTS) {
             String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(listener)); //$NON-NLS-1$
 Debug.println("addBundleListener[" + bundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }

         if (listener instanceof SynchronousBundleListener) {
             framework.checkAdminPermission(getBundle(), AdminPermission.LISTENER);
             synchronized (framework.bundleEventSync) {
                 if (bundleEventSync == null) {
                     bundleEventSync = new EventListeners();
                     framework.bundleEventSync.addListener(this, this);
                 }

                 bundleEventSync.addListener(listener, listener);
             }
         } else {
             synchronized (framework.bundleEvent) {
                 if (bundleEvent == null) {
                     bundleEvent = new EventListeners();
                     framework.bundleEvent.addListener(this, this);
                 }

                 bundleEvent.addListener(listener, listener);
             }
         }
     }

     /**
      * Remove a bundle listener.
      * The listener is removed from the context bundle's list of listeners.
      * See {@link #getBundle() getBundle()}
      * for a definition of context bundle.
      *
      * <p>If this method is called with a listener which is not registered,
      * then this method does nothing.
      *
      * @param listener The bundle listener to remove.
      * @exception java.lang.IllegalStateException
      * If the bundle context has stopped.
      */
     public void removeBundleListener(BundleListener listener) {
         checkValid();

         if (Debug.DEBUG && Debug.DEBUG_EVENTS) {
             String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(listener)); //$NON-NLS-1$
 Debug.println("removeBundleListener[" + bundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }

         if (listener instanceof SynchronousBundleListener) {
             framework.checkAdminPermission(getBundle(), AdminPermission.LISTENER);

             synchronized (framework.bundleEventSync) {
                 if (bundleEventSync != null) {
                     bundleEventSync.removeListener(listener);
                 }
             }
         } else {
             synchronized (framework.bundleEvent) {
                 if (bundleEvent != null) {
                     bundleEvent.removeListener(listener);
                 }
             }
         }
     }

     /**
      * Add a general framework listener.
      * {@link FrameworkListener}s are notified of general framework events.
      * The listener is added to the context bundle's list of listeners.
      * See {@link #getBundle() getBundle()}
      * for a definition of context bundle.
      *
      * @param listener The framework listener to add.
      * @exception java.lang.IllegalStateException
      * If the bundle context has stopped.
      * @see FrameworkEvent
      * @see FrameworkListener
      */
     public void addFrameworkListener(FrameworkListener listener) {
         checkValid();

         if (Debug.DEBUG && Debug.DEBUG_EVENTS) {
             String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(listener)); //$NON-NLS-1$
 Debug.println("addFrameworkListener[" + bundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }

         synchronized (framework.frameworkEvent) {
             if (frameworkEvent == null) {
                 frameworkEvent = new EventListeners();
                 framework.frameworkEvent.addListener(this, this);
             }

             frameworkEvent.addListener(listener, listener);
         }
     }

     /**
      * Remove a framework listener.
      * The listener is removed from the context bundle's list of listeners.
      * See {@link #getBundle() getBundle()}
      * for a definition of context bundle.
      *
      * <p>If this method is called with a listener which is not registered,
      * then this method does nothing.
      *
      * @param listener The framework listener to remove.
      * @exception java.lang.IllegalStateException
      * If the bundle context has stopped.
      */
     public void removeFrameworkListener(FrameworkListener listener) {
         checkValid();

         if (Debug.DEBUG && Debug.DEBUG_EVENTS) {
             String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(listener)); //$NON-NLS-1$
 Debug.println("removeFrameworkListener[" + bundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }

         synchronized (framework.frameworkEvent) {
             if (frameworkEvent != null) {
                 frameworkEvent.removeListener(listener);
             }
         }
     }

     /**
      * Register a service with multiple names.
      * This method registers the given service object with the given properties
      * under the given class names.
      * A {@link ServiceRegistrationImpl} object is returned.
      * The {@link ServiceRegistrationImpl} object is for the private use of the bundle
      * registering the service and should not be shared with other bundles.
      * The registering bundle is defined to be the context bundle.
      * See {@link #getBundle()} for a definition of context bundle.
      * Other bundles can locate the service by using either the
      * {@link #getServiceReferences getServiceReferences} or
      * {@link #getServiceReference getServiceReference} method.
      *
      * <p>A bundle can register a service object that implements the
      * {@link ServiceFactory} interface to
      * have more flexiblity in providing service objects to different
      * bundles.
      *
      * <p>The following steps are followed to register a service:
      * <ol>
      * <li>If the service parameter is not a {@link ServiceFactory},
      * an <code>IllegalArgumentException</code> is thrown if the
      * service parameter is not an <code>instanceof</code>
      * all the classes named.
      * <li>The service is added to the framework's service registry
      * and may now be used by other bundles.
      * <li>A {@link ServiceEvent} of type {@link ServiceEvent#REGISTERED}
      * is synchronously sent.
      * <li>A {@link ServiceRegistrationImpl} object for this registration
      * is returned.
      * </ol>
      *
      * @param clazzes The class names under which the service can be located.
      * The class names in this array will be stored in the service's
      * properties under the key "objectClass".
      * @param service The service object or a {@link ServiceFactory} object.
      * @param properties The properties for this service.
      * The keys in the properties object must all be Strings.
      * Changes should not be made to this object after calling this method.
      * To update the service's properties call the
      * {@link ServiceRegistrationImpl#setProperties ServiceRegistration.setProperties}
      * method.
      * This parameter may be <code>null</code> if the service has no properties.
      * @return A {@link ServiceRegistrationImpl} object for use by the bundle
      * registering the service to update the
      * service's properties or to unregister the service.
      * @exception java.lang.IllegalArgumentException If one of the following is true:
      * <ul>
      * <li>The service parameter is null.
      * <li>The service parameter is not a {@link ServiceFactory} and is not an
      * <code>instanceof</code> all the named classes in the clazzes parameter.
      * </ul>
      * @exception java.lang.SecurityException If the caller does not have
      * {@link ServicePermission} permission to "register" the service for
      * all the named classes
      * and the Java runtime environment supports permissions.
      * @exception java.lang.IllegalStateException
      * If the bundle context has stopped.
      * @see ServiceRegistrationImpl
      * @see ServiceFactory
      */
     public org.osgi.framework.ServiceRegistration registerService(String [] clazzes, Object service, Dictionary properties) {
         checkValid();

         if (service == null) {
             if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
                 Debug.println("Service object is null"); //$NON-NLS-1$
 }

             throw new IllegalArgumentException (Msg.SERVICE_ARGUMENT_NULL_EXCEPTION);
         }

         int size = clazzes.length;

         if (size == 0) {
             if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
                 Debug.println("Classes array is empty"); //$NON-NLS-1$
 }

             throw new IllegalArgumentException (Msg.SERVICE_EMPTY_CLASS_LIST_EXCEPTION);
         }

         /* copy the array so that changes to the original will not affect us. */
         String [] copy = new String [clazzes.length];
         // doing this the hard way so we can intern the strings
 for (int i = clazzes.length - 1; i >= 0; i--)
             copy[i] = clazzes[i].intern();
         clazzes = copy;

         /* check for ServicePermissions. */
         framework.checkRegisterServicePermission(clazzes);

         if (!(service instanceof ServiceFactory)) {
             String invalidService = checkServiceClass(clazzes, service);
             if (invalidService != null) {
                 if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
                     Debug.println("Service object is not an instanceof " + invalidService); //$NON-NLS-1$
 }
                 throw new IllegalArgumentException (NLS.bind(Msg.SERVICE_NOT_INSTANCEOF_CLASS_EXCEPTION, invalidService));
             }
         }

         return (createServiceRegistration(clazzes, service, properties));
     }

     //Return the name of the class that is not satisfied by the service object
 static String checkServiceClass(final String [] clazzes, final Object serviceObject) {
         ClassLoader cl = (ClassLoader ) AccessController.doPrivileged(new PrivilegedAction() {
             public Object run() {
                 return serviceObject.getClass().getClassLoader();
             }
         });
         for (int i = 0; i < clazzes.length; i++) {
             try {
                 Class serviceClazz = cl == null ? Class.forName(clazzes[i]) : cl.loadClass(clazzes[i]);
                 if (!serviceClazz.isInstance(serviceObject))
                     return clazzes[i];
             } catch (ClassNotFoundException e) {
                 //This check is rarely done
 if (extensiveCheckServiceClass(clazzes[i], serviceObject.getClass()))
                     return clazzes[i];
             }
         }
         return null;
     }

     private static boolean extensiveCheckServiceClass(String clazz, Class serviceClazz) {
         if (clazz.equals(serviceClazz.getName()))
             return false;
         Class [] interfaces = serviceClazz.getInterfaces();
         for (int i = 0; i < interfaces.length; i++)
             if (!extensiveCheckServiceClass(clazz, interfaces[i]))
                 return false;
         Class superClazz = serviceClazz.getSuperclass();
         if (superClazz != null)
             if (!extensiveCheckServiceClass(clazz, superClazz))
                 return false;
         return true;
     }

     /**
      * Create a new ServiceRegistration object. This method is used so that it may be overridden
      * by a secure implementation.
      *
      * @param clazzes The class names under which the service can be located.
      * @param service The service object or a {@link ServiceFactory} object.
      * @param properties The properties for this service.
      * @return A {@link ServiceRegistrationImpl} object for use by the bundle.
      */
     protected ServiceRegistrationImpl createServiceRegistration(String [] clazzes, Object service, Dictionary properties) {
         return (new ServiceRegistrationImpl(this, clazzes, service, properties));
     }

     /**
      * Register a service with a single name.
      * This method registers the given service object with the given properties
      * under the given class name.
      *
      * <p>This method is otherwise identical to
      * {@link #registerService(java.lang.String[], java.lang.Object, java.util.Dictionary)}
      * and is provided as a convenience when the service parameter will only be registered
      * under a single class name.
      *
      * @see #registerService(java.lang.String[], java.lang.Object, java.util.Dictionary)
      */
     public org.osgi.framework.ServiceRegistration registerService(String clazz, Object service, Dictionary properties) {
         String [] clazzes = new String [] {clazz};

         return (registerService(clazzes, service, properties));
     }

     /**
      * Returns a list of <tt>ServiceReference</tt> objects. This method returns a list of
      * <tt>ServiceReference</tt> objects for services which implement and were registered under
      * the specified class and match the specified filter criteria.
      *
      * <p>The list is valid at the time of the call to this method, however as the Framework is
      * a very dynamic environment, services can be modified or unregistered at anytime.
      *
      * <p><tt>filter</tt> is used to select the registered service whose
      * properties objects contain keys and values which satisfy the filter.
      * See {@link FilterImpl}for a description of the filter string syntax.
      *
      * <p>If <tt>filter</tt> is <tt>null</tt>, all registered services
      * are considered to match the filter.
      * <p>If <tt>filter</tt> cannot be parsed, an {@link InvalidSyntaxException}will
      * be thrown with a human readable message where the filter became unparsable.
      *
      * <p>The following steps are required to select a service:
      * <ol>
      * <li>If the Java Runtime Environment supports permissions, the caller is checked for the
      * <tt>ServicePermission</tt> to get the service with the specified class.
      * If the caller does not have the correct permission, <tt>null</tt> is returned.
      * <li>If the filter string is not <tt>null</tt>, the filter string is
      * parsed and the set of registered services which satisfy the filter is
      * produced.
      * If the filter string is <tt>null</tt>, then all registered services
      * are considered to satisfy the filter.
      * <li>If <code>clazz</code> is not <tt>null</tt>, the set is further reduced to
      * those services which are an <tt>instanceof</tt> and were registered under the specified class.
      * The complete list of classes of which a service is an instance and which
      * were specified when the service was registered is available from the
      * service's {@link Constants#OBJECTCLASS}property.
      * <li>An array of <tt>ServiceReference</tt> to the selected services is returned.
      * </ol>
      *
      * @param clazz The class name with which the service was registered, or
      * <tt>null</tt> for all services.
      * @param filter The filter criteria.
      * @return An array of <tt>ServiceReference</tt> objects, or
      * <tt>null</tt> if no services are registered which satisfy the search.
      * @exception InvalidSyntaxException If <tt>filter</tt> contains
      * an invalid filter string which cannot be parsed.
      */
     public org.osgi.framework.ServiceReference[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
         checkValid();
         if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
             Debug.println("getServiceReferences(" + clazz + ", \"" + filter + "\")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
         return (framework.getServiceReferences(clazz, filter, this, false));
     }

     public ServiceReference[] getAllServiceReferences(String clazz, String filter) throws InvalidSyntaxException {
         checkValid();
         if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
             Debug.println("getAllServiceReferences(" + clazz + ", \"" + filter + "\")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
         return (framework.getServiceReferences(clazz, filter, this, true));
     }

     /**
      * Get a service reference.
      * Retrieves a {@link ServiceReferenceImpl} for a service
      * which implements the named class.
      *
      * <p>This reference is valid at the time
      * of the call to this method, but since the framework is a very dynamic
      * environment, services can be modified or unregistered at anytime.
      *
      * <p>This method is provided as a convenience for when the caller is
      * interested in any service which implements a named class. This method is
      * the same as calling {@link #getServiceReferences getServiceReferences}
      * with a <code>null</code> filter string but only a single {@link ServiceReferenceImpl}
      * is returned.
      *
      * @param clazz The class name which the service must implement.
      * @return A {@link ServiceReferenceImpl} object, or <code>null</code>
      * if no services are registered which implement the named class.
      * @see #getServiceReferences
      */
     public org.osgi.framework.ServiceReference getServiceReference(String clazz) {
         checkValid();

         if (Debug.DEBUG && Debug.DEBUG_SERVICES) {
             Debug.println("getServiceReference(" + clazz + ")"); //$NON-NLS-1$ //$NON-NLS-2$
 }

         try {
             ServiceReference[] references = framework.getServiceReferences(clazz, null, this, false);

             if (references != null) {
                 int index = 0;

                 int length = references.length;

                 if (length > 1) /* if more than one service, select highest ranking */{
                     int rankings[] = new int[length];
                     int count = 0;
                     int maxRanking = Integer.MIN_VALUE;

                     for (int i = 0; i < length; i++) {
                         int ranking = ((ServiceReferenceImpl) references[i]).getRanking();

                         rankings[i] = ranking;

                         if (ranking > maxRanking) {
                             index = i;
                             maxRanking = ranking;
                             count = 1;
                         } else {
                             if (ranking == maxRanking) {
                                 count++;
                             }
                         }
                     }

                     if (count > 1) /* if still more than one service, select lowest id */{
                         long minId = Long.MAX_VALUE;

                         for (int i = 0; i < length; i++) {
                             if (rankings[i] == maxRanking) {
                                 long id = ((ServiceReferenceImpl) references[i]).getId();

                                 if (id < minId) {
                                     index = i;
                                     minId = id;
                                 }
                             }
                         }
                     }
                 }

                 return (references[index]);
             }
         } catch (InvalidSyntaxException e) {
             if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
                 Debug.println("InvalidSyntaxException w/ null filter" + e.getMessage()); //$NON-NLS-1$
 Debug.printStackTrace(e);
             }
         }

         return (null);
     }

     /**
      * Get a service's service object.
      * Retrieves the service object for a service.
      * A bundle's use of a service is tracked by a
      * use count. Each time a service's service object is returned by
      * {@link #getService}, the context bundle's use count for the service
      * is incremented by one. Each time the service is release by
      * {@link #ungetService}, the context bundle's use count
      * for the service is decremented by one.
      * When a bundle's use count for a service
      * drops to zero, the bundle should no longer use the service.
      * See {@link #getBundle()} for a definition of context bundle.
      *
      * <p>This method will always return <code>null</code> when the
      * service associated with this reference has been unregistered.
      *
      * <p>The following steps are followed to get the service object:
      * <ol>
      * <li>If the service has been unregistered,
      * <code>null</code> is returned.
      * <li>The context bundle's use count for this service is incremented by one.
      * <li>If the context bundle's use count for the service is now one and
      * the service was registered with a {@link ServiceFactory},
      * the {@link ServiceFactory#getService ServiceFactory.getService} method
      * is called to create a service object for the context bundle.
      * This service object is cached by the framework.
      * While the context bundle's use count for the service is greater than zero,
      * subsequent calls to get the services's service object for the context bundle
      * will return the cached service object.
      * <br>If the service object returned by the {@link ServiceFactory}
      * is not an <code>instanceof</code>
      * all the classes named when the service was registered or
      * the {@link ServiceFactory} throws an exception,
      * <code>null</code> is returned and a
      * {@link FrameworkEvent} of type {@link FrameworkEvent#ERROR} is broadcast.
      * <li>The service object for the service is returned.
      * </ol>
      *
      * @param reference A reference to the service whose service object is desired.
      * @return A service object for the service associated with this
      * reference, or <code>null</code> if the service is not registered.
      * @exception java.lang.SecurityException If the caller does not have
      * {@link ServicePermission} permission to "get" the service
      * using at least one of the named classes the service was registered under
      * and the Java runtime environment supports permissions.
      * @exception java.lang.IllegalStateException
      * If the bundle context has stopped.
      * @see #ungetService
      * @see ServiceFactory
      */
     public Object getService(org.osgi.framework.ServiceReference reference) {
         checkValid();

         synchronized (contextLock) {
             if (servicesInUse == null)
                 // Cannot predict how many services a bundle will use, start with a small table.
 servicesInUse = new Hashtable(10);
         }

         ServiceRegistrationImpl registration = ((ServiceReferenceImpl) reference).registration;

         framework.checkGetServicePermission(registration.clazzes);

         return registration.getService(BundleContextImpl.this);
     }

     /**
      * Unget a service's service object.
      * Releases the service object for a service.
      * If the context bundle's use count for the service is zero, this method
      * returns <code>false</code>. Otherwise, the context bundle's use count for the
      * service is decremented by one.
      * See {@link #getBundle()} for a definition of context bundle.
      *
      * <p>The service's service object
      * should no longer be used and all references to it should be destroyed
      * when a bundle's use count for the service
      * drops to zero.
      *
      * <p>The following steps are followed to unget the service object:
      * <ol>
      * <li>If the context bundle's use count for the service is zero or
      * the service has been unregistered,
      * <code>false</code> is returned.
      * <li>The context bundle's use count for this service is decremented by one.
      * <li>If the context bundle's use count for the service is now zero and
      * the service was registered with a {@link ServiceFactory},
      * the {@link ServiceFactory#ungetService ServiceFactory.ungetService} method
      * is called to release the service object for the context bundle.
      * <li><code>true</code> is returned.
      * </ol>
      *
      * @param reference A reference to the service to be released.
      * @return <code>false</code> if the context bundle's use count for the service
      * is zero or if the service has been unregistered,
      * otherwise <code>true</code>.
      * @exception java.lang.IllegalStateException
      * If the bundle context has stopped.
      * @see #getService
      * @see ServiceFactory
      */
     public boolean ungetService(org.osgi.framework.ServiceReference reference) {
         checkValid();

         ServiceRegistrationImpl registration = ((ServiceReferenceImpl) reference).registration;

         return registration.ungetService(BundleContextImpl.this);
     }

     /**
      * Creates a <code>File</code> object for a file in the
      * persistent storage area provided for the bundle by the framework.
      * If the adaptor does not have file system support, this method will
      * return <code>null</code>.
      *
      * <p>A <code>File</code> object for the base directory of the
      * persistent storage area provided for the context bundle by the framework
      * can be obtained by calling this method with the empty string ("")
      * as the parameter.
      * See {@link #getBundle()} for a definition of context bundle.
      *
      * <p>If the Java runtime environment supports permissions,
      * the framework the will ensure that the bundle has
      * <code>java.io.FilePermission</code> with actions
      * "read","write","execute","delete" for all files (recursively) in the
      * persistent storage area provided for the context bundle by the framework.
      *
      * @param filename A relative name to the file to be accessed.
      * @return A <code>File</code> object that represents the requested file or
      * <code>null</code> if the adaptor does not have file system support.
      * @exception java.lang.IllegalStateException
      * If the bundle context has stopped.
      */
     public File getDataFile(String filename) {
         checkValid();

         return (framework.getDataFile(bundle, filename));
     }

     /**
      * Call bundle's BundleActivator.start()
      * This method is called by Bundle.startWorker to start the bundle.
      *
      * @exception org.osgi.framework.BundleException if
      * the bundle has a class that implements the BundleActivator interface,
      * but Framework couldn't instantiate it, or the BundleActivator.start()
      * method failed
      */
     protected void start() throws BundleException {
         activator = bundle.loadBundleActivator();

         if (activator != null) {
             try {
                 startActivator(activator);
             } catch (BundleException be) {
                 activator = null;
                 throw be;
             }
         }

         /* activator completed successfully. We must use this
          same activator object when we stop this bundle. */
     }

     /**
      * Calls the start method of a BundleActivator.
      * @param bundleActivator that activator to start
      */
     protected void startActivator(final BundleActivator bundleActivator) throws BundleException {
         if (Profile.PROFILE && Profile.STARTUP)
             Profile.logEnter("BundleContextImpl.startActivator()", null); //$NON-NLS-1$
 try {
             AccessController.doPrivileged(new PrivilegedExceptionAction() {
                 public Object run() throws Exception {
                     if (bundleActivator != null) {
                         if (Profile.PROFILE && Profile.STARTUP)
                             Profile.logTime("BundleContextImpl.startActivator()", "calling " + bundle.getLocation() + " bundle activator"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
 /* Start the bundle synchronously */
                         bundleActivator.start(BundleContextImpl.this);
                        if (Profile.PROFILE && Profile.STARTUP)
                            Profile.logTime("BundleContextImpl.startActivator()", "returned from " + bundle.getLocation() + " bundle activator"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
 }
                    return null;
                }
            });
        } catch (Throwable t) {
            if (t instanceof PrivilegedActionException) {
                t = ((PrivilegedActionException) t).getException();
            }

            if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
                Debug.printStackTrace(t);
            }

            String clazz = null;
            clazz = bundleActivator.getClass().getName();

            throw new BundleException(NLS.bind(Msg.BUNDLE_ACTIVATOR_EXCEPTION, new Object [] {clazz, "start", bundle.getSymbolicName() == null ? "" + bundle.getBundleId() : bundle.getSymbolicName()}), t); //$NON-NLS-1$ //$NON-NLS-2$
 } finally {
            if (Profile.PROFILE && Profile.STARTUP)
                Profile.logExit("BundleContextImpl.startActivator()"); //$NON-NLS-1$
 }

    }

    /**
     * Call bundle's BundleActivator.stop()
     * This method is called by Bundle.stopWorker to stop the bundle.
     *
     * @exception org.osgi.framework.BundleException if
     * the bundle has a class that implements the BundleActivator interface,
     * and the BundleActivator.stop() method failed
     */
    protected void stop() throws BundleException {
        try {
            AccessController.doPrivileged(new PrivilegedExceptionAction() {
                public Object run() throws Exception {
                    if (activator != null) {
                        /* Stop the bundle synchronously */
                        activator.stop(BundleContextImpl.this);
                    }
                    return null;
                }
            });
        } catch (Throwable t) {
            if (t instanceof PrivilegedActionException) {
                t = ((PrivilegedActionException) t).getException();
            }

            if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
                Debug.printStackTrace(t);
            }

            String clazz = (activator == null) ? "" : activator.getClass().getName(); //$NON-NLS-1$

            throw new BundleException(NLS.bind(Msg.BUNDLE_ACTIVATOR_EXCEPTION, new Object [] {clazz, "stop", bundle.getSymbolicName() == null ? "" + bundle.getBundleId() : bundle.getSymbolicName()}), t); //$NON-NLS-1$ //$NON-NLS-2$
 } finally {
            activator = null;
        }
    }

    /**
     * Provides a list of {@link ServiceReference}s for the services
     * registered by this bundle
     * or <code>null</code> if the bundle has no registered
     * services.
     *
     * <p>The list is valid at the time
     * of the call to this method, but the framework is a very dynamic
     * environment and services can be modified or unregistered at anytime.
     *
     * @return An array of {@link ServiceReference} or <code>null</code>.
     * @exception java.lang.IllegalStateException If the
     * bundle has been uninstalled.
     * @see ServiceRegistrationImpl
     * @see ServiceReferenceImpl
     */
    protected ServiceReference[] getRegisteredServices() {
        ServiceReference[] services = null;

        synchronized (framework.serviceRegistry) {
            services = framework.serviceRegistry.lookupServiceReferences(this);
            if (services == null) {
                return null;
            }
            int removed = 0;
            for (int i = services.length - 1; i >= 0; i--) {
                ServiceReferenceImpl ref = (ServiceReferenceImpl) services[i];
                String [] classes = ref.getClasses();
                try { /* test for permission to the classes */
                    framework.checkGetServicePermission(classes);
                } catch (SecurityException se) {
                    services[i] = null;
                    removed++;
                }
            }
            if (removed > 0) {
                ServiceReference[] temp = services;
                services = new ServiceReference[temp.length - removed];
                for (int i = temp.length - 1; i >= 0; i--) {
                    if (temp[i] == null)
                        removed--;
                    else
                        services[i - removed] = temp[i];
                }
            }
        }
        return (services);

    }

    /**
     * Provides a list of {@link ServiceReferenceImpl}s for the
     * services this bundle is using,
     * or <code>null</code> if the bundle is not using any services.
     * A bundle is considered to be using a service if the bundle's
     * use count for the service is greater than zero.
     *
     * <p>The list is valid at the time
     * of the call to this method, but the framework is a very dynamic
     * environment and services can be modified or unregistered at anytime.
     *
     * @return An array of {@link ServiceReferenceImpl} or <code>null</code>.
     * @exception java.lang.IllegalStateException If the
     * bundle has been uninstalled.
     * @see ServiceReferenceImpl
     */
    protected ServiceReferenceImpl[] getServicesInUse() {
        if (servicesInUse == null) {
            return (null);
        }

        synchronized (servicesInUse) {
            int size = servicesInUse.size();

            if (size == 0) {
                return (null);
            }

            ServiceReferenceImpl[] references = new ServiceReferenceImpl[size];
            int refcount = 0;

            Enumeration refsEnum = servicesInUse.keys();

            for (int i = 0; i < size; i++) {
                ServiceReferenceImpl reference = (ServiceReferenceImpl) refsEnum.nextElement();

                try {
                    framework.checkGetServicePermission(reference.registration.clazzes);
                } catch (SecurityException se) {
                    continue;
                }

                references[refcount] = reference;
                refcount++;
            }

            if (refcount < size) {
                if (refcount == 0) {
                    return (null);
                }

                ServiceReferenceImpl[] refs = references;
                references = new ServiceReferenceImpl[refcount];

                System.arraycopy(refs, 0, references, 0, refcount);
            }

            return (references);
        }
    }

    /**
     * Bottom level event dispatcher for the BundleContext.
     *
     * @param originalListener listener object registered under.
     * @param l listener to call (may be filtered).
     * @param action Event class type
     * @param object Event object
     */
    public void dispatchEvent(Object originalListener, Object l, int action, Object object) {
        // save the bundle ref to a local variable
 // to avoid interference from another thread closing this context
 AbstractBundle tmpBundle = bundle;
        try {
            if (isValid()) /* if context still valid */{
                switch (action) {
                    case Framework.BUNDLEEVENT :
                    case Framework.BUNDLEEVENTSYNC : {
                        BundleListener listener = (BundleListener) l;

                        if (Debug.DEBUG && Debug.DEBUG_EVENTS) {
                            String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(listener)); //$NON-NLS-1$
 Debug.println("dispatchBundleEvent[" + tmpBundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }

                        BundleEvent event = (BundleEvent) object;
                        switch (event.getType()) {
                            case Framework.BATCHEVENT_BEGIN : {
                                if (listener instanceof BatchBundleListener)
                                    ((BatchBundleListener) listener).batchBegin();
                                break;
                            }
                            case Framework.BATCHEVENT_END : {
                                if (listener instanceof BatchBundleListener)
                                    ((BatchBundleListener) listener).batchEnd();
                                break;
                            }
                            default : {
                                listener.bundleChanged((BundleEvent) object);
                            }
                        }
                        break;
                    }

                    case Framework.SERVICEEVENT : {
                        ServiceEvent event = (ServiceEvent) object;

                        ServiceListener listener = (ServiceListener) l;
                        if (Debug.DEBUG && Debug.DEBUG_EVENTS) {
                            String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(listener)); //$NON-NLS-1$
 Debug.println("dispatchServiceEvent[" + tmpBundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }
                        listener.serviceChanged(event);

                        break;
                    }

                    case Framework.FRAMEWORKEVENT : {
                        FrameworkListener listener = (FrameworkListener) l;

                        if (Debug.DEBUG && Debug.DEBUG_EVENTS) {
                            String listenerName = listener.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(listener)); //$NON-NLS-1$
 Debug.println("dispatchFrameworkEvent[" + tmpBundle + "](" + listenerName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
 }

                        listener.frameworkEvent((FrameworkEvent) object);
                        break;
                    }
                }
            }
        } catch (Throwable t) {
            if (Debug.DEBUG && Debug.DEBUG_GENERAL) {
                Debug.println("Exception in bottom level event dispatcher: " + t.getMessage()); //$NON-NLS-1$
 Debug.printStackTrace(t);
            }
            // allow the adaptor to handle this unexpected error
 framework.adaptor.handleRuntimeError(t);
            publisherror: {
                if (action == Framework.FRAMEWORKEVENT) {
                    FrameworkEvent event = (FrameworkEvent) object;
                    if (event.getType() == FrameworkEvent.ERROR) {
                        break publisherror; // avoid infinite loop
 }
                }

                framework.publishFrameworkEvent(FrameworkEvent.ERROR, tmpBundle, t);
            }
        }
    }

    /**
     * Check for permission to listen to a service.
     */
    protected boolean hasListenServicePermission(ServiceEvent event) {
        ProtectionDomain domain = bundle.getProtectionDomain();

        if (domain != null) {
            ServiceReferenceImpl reference = (ServiceReferenceImpl) event.getServiceReference();

            String [] names = reference.getClasses();

            int len = names.length;

            for (int i = 0; i < len; i++) {
                if (domain.implies(new ServicePermission(names[i], ServicePermission.GET))) {
                    return true;
                }
            }

            return false;
        }

        return (true);
    }

    /**
     * Construct a Filter object. This filter object may be used
     * to match a ServiceReference or a Dictionary.
     * See Filter
     * for a description of the filter string syntax.
     *
     * @param filter The filter string.
     * @return A Filter object encapsulating the filter string.
     * @exception InvalidSyntaxException If the filter parameter contains
     * an invalid filter string which cannot be parsed.
     */
    public org.osgi.framework.Filter createFilter(String filter) throws InvalidSyntaxException {
        checkValid();

        return (new FilterImpl(filter));
    }

    /**
     * This method checks that the context is still valid. If the context is
     * no longer valid, an IllegalStateException is thrown.
     *
     * @exception java.lang.IllegalStateException
     * If the context bundle has stopped.
     */
    protected void checkValid() {
        if (!isValid()) {
            throw new IllegalStateException (Msg.BUNDLE_CONTEXT_INVALID_EXCEPTION);
        }
    }

    /**
     * This method checks that the context is still valid.
     *
     * @return true if the context is still valid; false otherwise
     */
    protected boolean isValid() {
        return valid;
    }

    boolean isAssignableTo(ServiceReferenceImpl reference) {
        if (!scopeEvents)
            return true;
        String [] clazzes = reference.getClasses();
        for (int i = 0; i < clazzes.length; i++)
            if (!reference.isAssignableTo(bundle, clazzes[i]))
                return false;
        return true;
    }
}

